package apigateway.project.core.redis;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Redis工具包
 */
public class RedisUtils {

    private static final Logger log = LoggerFactory.getLogger(RedisUtils.class);
    public static JedisPool jedisPool = null;
    private String host;
    private int port;
    private int maxTotal;
    private int maxIdle;
    private int minIdle;
    private static RedissonClient redisson = null;
    private final ReentrantLock reentrantLock = new ReentrantLock();

    private RedissonClient getRedisson(){
        if(redisson == null) {
            Config config = new Config();
            config.useSingleServer().setAddress("redis://" + host + ":" + port);
            redisson = Redisson.create(config);
        }
        return redisson;
    }
    /**
     *
     * @param sourceName 资源名
     * @param minRequestAmount 熔断触发的最小请求数
     * @param statIntervalMs 统计时长（单位为 ms)
     * @param slowRatioThreshold 慢调用比例阈值
     * @param timeWindow 	熔断时长，单位为 s
     * @param responseTime 响应时间,单位为 ms
     * @param count 慢调用临界 RT（超出该值计为慢调用）,单位为 ms
     * @return
     */
    public boolean isCircuitBreakerOpen(String sourceName,int minRequestAmount,
                                      int statIntervalMs,double slowRatioThreshold,int timeWindow,int count,long responseTime){
        Jedis jedis = null;
        RedissonClient redissonClient = null;
        try {
            jedis = getJedis();
            redissonClient = getRedisson();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        RLock lock = redissonClient.getLock("lock:" + sourceName);
        String breakerKey = "breaker";
        String breakerStatKey = "breaker:" + sourceName + ":stat";
        String breakerSetTimeKey = "breaker:" + sourceName + ":setTime";
        try {
            lock.lock();
            //获取当前断路器的状态
            String stats = jedis.hget(breakerKey,breakerStatKey);
            long now = System.currentTimeMillis();

            if(stats == null){
                stats = "Open";
                jedis.hset(breakerKey,breakerStatKey,stats);
                jedis.hset(breakerKey,breakerSetTimeKey, String.valueOf(now));
            }
            //如果断路器是闭合的,直接熔断返回false
            //这里的Close代表断路器工作,熔断生效
            if(stats.equals("Close")){
                //获取断路器闭合时间
                String value = jedis.hget(breakerKey, breakerSetTimeKey);
                long breakerSetTime = Long.parseLong(value);
                //熔断
                if(now - breakerSetTime > timeWindow * 1000L){
                    //超过熔断时长,断路器进入半闭合状态
                    stats = "HalfOpen";
                    jedis.hset(breakerKey,breakerStatKey,stats);
                }else {
                    return false;
                }
            }
            if(stats.equals("HalfOpen")){
                String halfOpenStatsKey = "breaker:" + sourceName + ":halfOpenStats";
                long allowedRequests = 5;
                jedis.hincrBy(halfOpenStatsKey, "totalRequests", 1);
                if (responseTime > count) {
                jedis.hincrBy(halfOpenStatsKey, "slowRequests", 1);
    }
                // 检查累计请求是否达到限制
                long totalRequests = Long.parseLong(jedis.hget(halfOpenStatsKey, "totalRequests"));
                if(totalRequests>=allowedRequests){
                     long slowRequests = Long.parseLong(jedis.hget(halfOpenStatsKey, "slowRequests"));
                     if(slowRequests>0){
                         stats = "Close";
                     }else{
                         stats = "Open"; // 没有慢请求，恢复到 Open
                     }
                     jedis.hset(breakerKey,breakerStatKey,stats);
                     jedis.hset(breakerKey,breakerSetTimeKey,String.valueOf(System.currentTimeMillis()));
                     // 重置统计数据
                     jedis.hdel(halfOpenStatsKey, "totalRequests", "slowRequests");
                     return stats.equals("Open");
                }
            }
            jedis.zremrangeByScore(sourceName,0,now - statIntervalMs);
            jedis.zadd(sourceName,now, responseTime +":"+now);
            Set<String> responseTimes = jedis.zrange(sourceName, 0, -1);
            long total = jedis.zcard(sourceName);
            if(total < minRequestAmount){
                //达不到熔断触发的最小请求数,直接放行
                return true;
            }
            long slowRequests = 0;
            //统计慢调用请求数
            for (String time : responseTimes){
                time = time.split(":")[0];
                if(Long.parseLong(time) > count){
                    slowRequests++;
                }
            }
            double slowRatio = 0.0;
            //计算慢调用比例
            if(total != 0) {
                slowRatio = (double) slowRequests / total;
            }
            // 慢调用比例超过熔断阈值就熔断,返回false
            if(slowRatio > slowRatioThreshold){
                jedis.hset(breakerKey,breakerStatKey,"Close");
                jedis.hset(breakerKey,breakerSetTimeKey, String.valueOf(System.currentTimeMillis()));
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
            if(jedis != null) jedis.close();
        }
    }

    private void getConfig(){
        Properties properties = new Properties();

        try {
            properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("gateway.properties"));
            host = properties.getProperty("redis.host");
            port = Integer.parseInt(properties.getProperty("redis.port"));
            maxTotal = Integer.parseInt(properties.getProperty("redis.maxTotal"));
            maxIdle = Integer.parseInt(properties.getProperty("redis.maxIdle"));
            minIdle = Integer.parseInt(properties.getProperty("redis.minIdle"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    private void initJedisPool(){
            try {
                reentrantLock.lock();
                getConfig();
                JedisPoolConfig config = new JedisPoolConfig();
                config.setMaxTotal(maxTotal);
                config.setMaxIdle(maxIdle);
                config.setMinIdle(minIdle);
                jedisPool = new JedisPool(config,host,port);
            } catch (Exception e) {
                log.warn("failed to init redis pool:{}",e.getMessage());
            } finally {
                reentrantLock.unlock();
            }

    }

    private Jedis getJedis(){
        if(jedisPool == null) initJedisPool();
        return jedisPool.getResource();
    }






//    private static String buildCircuitBreakerLua() {
//     String lua =
//    "local key=KEYS[1]\n" +
//    "local minRequestAmount=tonumber(ARGV[1])\n" +
//    "local statIntervalMs=tonumber(ARGV[2])\n" +
//    "local slowRatioThreshold=tonumber(ARGV[3])\n" +
//    "\n" +
//    "local timeWindow=tonumber(ARGV[4])\n" +
//    "local timeStamp=tonumber(ARGV[5])\n" +
//    "local isSlowReq=ARGV[6]\n" +
//    "local uniqueMember=isSlowReq..':'..ARGV[5]\n"+
//    "\n" +
//    "local circuit_breaker_key='circuit_breaker_status'\n" +
//    "if redis.call('GET', circuit_breaker_key)=='close' then\n" +
//    "\treturn 0\n" +
//    "end\n" +
//    "\n" +
//    "redis.call('ZREMRANGEBYSCORE', key, 0, timeStamp - statIntervalMs)\n" +
//    "redis.call('ZADD', key, timeStamp, uniqueMember)\n" +
//    "local total=redis.call('ZCARD', key)\n" +
//    "\n" +
//    "if total < minRequestAmount then\n" +
//    "\treturn 1\n" +
//    "end\n" +
//    "\n" +
//    "local members=redis.call('ZRANGE', key, 0, -1)\n" +
//    "local count=0\n"+
//    "for i=1, #members do\n" +
//    "\tif string.match(members[i],'^true:') then\n" +
//    "\t\tcount=count + 1\n" +
//    "\tend\n" +
//    "end\n" +
//    "\n" +
//    "local ratio=0\n" +
//    "\n" +
//    "if total>0 then\n" +
//    "\tratio=count/total\n" +
//    "else\n"+
//    "\treturn 1\n" +
//    "end\n" +
//    "\n" +
//    "if ratio>slowRatioThreshold then\n" +
//    "\tredis.call('SETEX', circuit_breaker_key, timeWindow, 'close')\n" +
//    "\treturn 0\n" +
//    "else\n" +
//    "\treturn 1\n" +
//    "end";
//    return lua;
//
//}
}
